package ms.maplestory.boot.loaders;

import ms.maplestory.boot.ServerConstants;
import ms.maplestory.boot.client.character.Inventory.Equip;
import ms.maplestory.boot.client.character.Inventory.ItemOption;
import ms.maplestory.boot.constants.ItemConstants;
import ms.maplestory.boot.enums.*;
import ms.maplestory.boot.loaders.container.ItemInfo;
import ms.maplestory.boot.loaders.container.ItemRewardInfo;
import ms.maplestory.boot.loaders.container.PetInfo;
import ms.maplestory.boot.util.*;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import java.io.*;
import java.util.*;

import static ms.maplestory.boot.enums.ScrollStatType.*;
import static ms.maplestory.boot.loaders.MainLoader.INFO_OUT;

@Slf4j
public class ItemData {

    public static Map<Integer, Equip> equips = new HashMap<>();
    public static Map<Integer, ItemInfo> items = new HashMap<>();
    public static Map<Integer, PetInfo> pets = new HashMap<>();
    public static Map<Integer, ItemOption> itemOptions = new HashMap<>();
    private static Set<Integer> startingItems = new HashSet<>();

    public static void generateDatFiles() {
        log.info("Started generating item data.");
        long start = System.currentTimeMillis();
        loadEquipsFromWz();
        loadItemsFromWZ();
        loadPetsFromWZ();
        loadItemOptionsFromWZ();
        QuestData.linkItemData();
        saveEquipsToDat(ServerConstants.DAT_DIR + "/equips");
        saveItemsToDat(ServerConstants.DAT_DIR + "/items");
        savePetsToDat(ServerConstants.DAT_DIR + "/pets");
        saveItemOptionsToDat(ServerConstants.DAT_DIR);
        log.info(String.format("Completed generating item data in %dms.", System.currentTimeMillis() - start));
    }

    public static void loadEquipsFromWz() {
        String wzDir = ServerConstants.WZ_DIR + "/Character.wz";
        String[] subMaps = new String[]{"Accessory", "Cap", "Cape", "Coat", "Dragon", "Face", "Glove",
                "Longcoat", "Mechanic", "Pants", "PetEquip", "Ring", "Shield", "Shoes", "Weapon"};
        for (String subMap : subMaps) {
            File subDir = new File(String.format("%s/%s", wzDir, subMap));
            File[] files = subDir.listFiles();
            for (File file : files) {
                Node node = XMLApi.getRoot(file);
                List<Node> nodes = XMLApi.getAllChildren(node);
                for (Node mainNode : nodes) {
                    Map<String, String> attributes = XMLApi.getAttributes(mainNode);
                    String mainName = attributes.get("name");
                    if (mainName != null) {
                        int itemId = Integer.parseInt(attributes.get("name").replace(".img", ""));
                        Equip equip = new Equip();
                        equip.setItemId(itemId);
                        equip.setInventoryType(InventoryType.Equip);
                        equip.setType(ItemType.Equip);
                        equip.setDateExpire(FileTime.fromType(FileTime.Type.MAX_TIME));
                        List<Integer> options = new ArrayList<>(7);
                        for (Node n : XMLApi.getAllChildren(XMLApi.getFirstChildByNameBF(mainNode, "info"))) {
                            String name = XMLApi.getNamedAttribute(n, "name");
                            String value = XMLApi.getNamedAttribute(n, "value");
                            switch (name) {
                                case "islot":
                                    equip.setISlot(value);
                                    break;
                                case "vslot":
                                    equip.setVSlot(value);
                                    break;
                                case "reqJob":
                                    equip.setReqJob(Integer.parseInt(value));
                                    break;
                                case "reqLevel":
                                    equip.setReqLevel(Integer.parseInt(value));
                                    break;
                                case "reqSTR":
                                    equip.setReqSTR(Integer.parseInt(value));
                                    break;
                                case "reqDEX":
                                    equip.setReqDEX(Integer.parseInt(value));
                                    break;
                                case "reqINT":
                                    equip.setReqINT(Integer.parseInt(value));
                                    break;
                                case "reqLUK":
                                    equip.setReqLUK(Integer.parseInt(value));
                                    break;
                                case "reqPOP":
                                    equip.setReqPOP(Integer.parseInt(value));
                                    break;
                                case "incSTR":
                                    equip.setIncSTR(Integer.parseInt(value));
                                    break;
                                case "incDEX":
                                    equip.setIncDEX(Integer.parseInt(value));
                                    break;
                                case "incINT":
                                    equip.setIncINT(Integer.parseInt(value));
                                    break;
                                case "incLUK":
                                    equip.setIncLUK(Integer.parseInt(value));
                                    break;
                                case "incPDD":
                                    equip.setIncPDD(Integer.parseInt(value));
                                    break;
                                case "incMDD":
                                    equip.setIncMDD(Integer.parseInt(value));
                                    break;
                                case "incMHP":
                                    equip.setIncMHP(Integer.parseInt(value));
                                    break;
                                case "incMHPr":
                                    equip.setIncMHPr(Integer.parseInt(value));
                                    break;
                                case "incMMP":
                                    equip.setIncMMP(Integer.parseInt(value));
                                    break;
                                case "incMMPr":
                                    equip.setIncMMPr(Integer.parseInt(value));
                                    break;
                                case "incPAD":
                                    equip.setIncPAD(Integer.parseInt(value));
                                    break;
                                case "incMAD":
                                    equip.setIncMAD(Integer.parseInt(value));
                                    break;
                                case "incEVA":
                                    equip.setIncEVA(Integer.parseInt(value));
                                    break;
                                case "incACC":
                                    equip.setIncACC(Integer.parseInt(value));
                                    break;
                                case "incSpeed":
                                    equip.setIncSpeed(Integer.parseInt(value));
                                    break;
                                case "incJump":
                                    equip.setIncJump(Integer.parseInt(value));
                                    break;
                                case "tuc":
                                    equip.setTuc(Integer.parseInt(value));
                                    break;
                                case "IUCMax":
                                    equip.setHasIucMax(true);
                                    equip.setIucMax(Short.parseShort(value));
                                    break;
                                case "setItemID":
                                    equip.setSetItemID(Integer.parseInt(value));
                                    break;
                                case "price":
                                    equip.setPrice(Integer.parseInt(value));
                                    break;
                                case "attackSpeed":
                                    equip.setAttackSpeed(Integer.parseInt(value));
                                    break;
                                case "cash":
                                    equip.setCash(Integer.parseInt(value) != 0);
                                    break;
                                case "expireOnLogout":
                                    equip.setExpireOnLogout(Integer.parseInt(value) != 0);
                                    break;
                                case "notSale":
                                    equip.setNotSale(Integer.parseInt(value) != 0);
                                    break;
                                case "only":
                                    equip.setOnly(Integer.parseInt(value) != 0);
                                    break;
                                case "tradeBlock":
                                    equip.setTradeBlock(Integer.parseInt(value) != 0);
                                    break;
                            }
                        }
                        equips.put(equip.getItemId(), equip);
                    }
                }
            }
        }
    }

    public static void loadItemsFromWZ() {
        String wzDir = ServerConstants.WZ_DIR + "/Item.wz";
        String[] subMaps = new String[]{"Cash", "Consume", "Etc", "Install", "Special"}; // not pet
        for (String subMap : subMaps) {
            File subDir = new File(String.format("%s/%s", wzDir, subMap));
            File[] files = subDir.listFiles();
            for (File file : files) {
                Document doc = XMLApi.getRoot(file);
                Node node = doc;
                List<Node> nodes = XMLApi.getAllChildren(node);
                for (Node mainNode : XMLApi.getAllChildren(nodes.get(0))) {
                    String nodeName = XMLApi.getNamedAttribute(mainNode, "name");
                    if (!Util.isNumber(nodeName)) {
                        log.error(String.format("%s is not a number.", nodeName));
                        continue;
                    }
                    int id = Integer.parseInt(nodeName);
                    ItemInfo item = new ItemInfo();
                    item.setId(id);
                    item.setInventoryType(InventoryType.getTypeByString(subMap));
                    Node infoNode = XMLApi.getFirstChildByNameBF(mainNode, "info");
                    if (infoNode != null) {
                        for (Node info : XMLApi.getAllChildren(infoNode)) {
                            String name = XMLApi.getNamedAttribute(info, "name");
                            String value = XMLApi.getNamedAttribute(info, "value");
                            int intValue = 0;
                            if (Util.isInteger(value)) {
                                intValue = Integer.parseInt(value);
                            }
                            switch (name) {
                                case "cash":
                                    item.setCash(intValue != 0);
                                    break;
                                case "price":
                                    item.setPrice(intValue);
                                    break;
                                case "slotMax":
                                    item.setSlotMax(intValue);
                                    break;
                                // info not currently interesting. May be interesting in the future.
                                case "icon":
                                case "iconRaw":
                                case "iconD":
                                case "iconReward":
                                case "iconShop":
                                case "recoveryHP":
                                case "recoveryMP":
                                case "sitAction":
                                case "bodyRelMove":
                                case "only":
                                case "noDrop":
                                case "timeLimited":
                                case "accountSharable":
                                case "nickTag":
                                case "nickSkill":
                                case "endLotteryDate":
                                case "noFlip":
                                case "noMoveToLocker":
                                case "soldInform":
                                case "purchaseShop":
                                case "flatRate":
                                case "limitMin":
                                case "protectTime":
                                case "maxDays":
                                case "reset":
                                case "replace":
                                case "expireOnLogout":
                                case "max":
                                case "lvOptimum":
                                case "lvRange":
                                case "limitedLv":
                                case "tradeReward":
                                case "type":
                                case "floatType":
                                case "message":
                                case "pquest":
                                case "bonusEXPRate":
                                case "notExtend":
                                    break;
                                case "reqSkillLevel":
                                    item.setReqSkillLevel(intValue);
                                    break;
                                case "masterLevel":
                                    item.setMasterLevel(intValue);
                                    break;
                                case "stateChangeItem":
                                case "direction":
                                case "exGrade":
                                case "exGradeWeight":
                                case "effect":
                                case "bigSize":
                                case "nickSkillTimeLimited":
                                case "StarPlanet":
                                case "useTradeBlock":
                                case "commerce":
                                case "invisibleWeapon":
                                case "sitEmotion":
                                case "sitLeft":
                                case "tamingMob":
                                case "textInfo":
                                case "lv":
                                case "tradeAvailable":
                                case "pickUpBlock":
                                case "rewardItemID":
                                case "autoPrice":
                                case "selectedSlot":
                                case "minusLevel":
                                case "addTime":
                                case "reqLevel":
                                case "waittime":
                                case "buffchair":
                                case "cooltime":
                                case "consumeitem":
                                case "distanceX":
                                case "distanceY":
                                case "maxDiff":
                                case "maxDX":
                                case "levelDX":
                                case "maxLevel":
                                case "exp":
                                case "dropBlock":
                                case "dropExpireTime":
                                case "animation_create":
                                case "animation_dropped":
                                case "noCancelMouse":
                                case "soulItemType":
                                case "Rate":
                                case "unitPrice":
                                case "delayMsg":
                                case "bridlePropZeroMsg":
                                case "create":
                                case "nomobMsg":
                                case "bridleProp":
                                case "bridlePropChg":
                                case "bridleMsgType":
                                case "mobHP":
                                case "left":
                                case "right":
                                case "top":
                                case "bottom":
                                case "useDelay":
                                case "name":
                                case "uiData":
                                case "UI":
                                case "recoveryRate":
                                case "itemMsg":
                                case "noRotateIcon":
                                case "endUseDate":
                                case "noSound":
                                case "slotMat":
                                case "isBgmOrEffect":
                                case "bgmPath":
                                case "repeat":
                                case "NoCancel":
                                case "rotateSpeed":
                                case "gender":
                                case "life":
                                case "pickupItem":
                                case "add":
                                case "consumeHP":
                                case "longRange":
                                case "dropSweep":
                                case "pickupAll":
                                case "ignorePickup":
                                case "consumeMP":
                                case "autoBuff":
                                case "smartPet":
                                case "giantPet":
                                case "shop":
                                case "recall":
                                case "autoSpeaking":
                                case "consumeCure":
                                case "meso":
                                case "maplepoint":
                                case "rate":
                                case "overlap":
                                case "lt":
                                case "rb":
                                case "path4Top":
                                case "jumplevel":
                                case "slotIndex":
                                case "addDay":
                                case "incLEV":
                                case "cashTradeBlock":
                                case "dressUpgrade":
                                case "skillEffectID":
                                case "emotion":
                                case "tradBlock":
                                case "tragetBlock":
                                case "scanTradeBlock":
                                case "mobPotion":
                                case "ignoreTendencyStatLimit":
                                case "effectByItemID":
                                case "pachinko":
                                case "iconEnter":
                                case "iconLeave":
                                case "noMoveIcon":
                                case "noShadow":
                                case "preventslip":
                                case "warmsupport":
                                case "reqCUC":
                                case "incCraft":
                                case "reqEquipLevelMin":
                                case "incPVPDamage":
                                case "successRates":
                                case "enchantCategory":
                                case "additionalSuccess":
                                case "level":
                                case "specialItem":
                                case "exNew":
                                case "cuttable":
                                case "perfectReset":
                                case "resetRUC":
                                case "incMax":
                                case "noSuperior":
                                case "noRecovery":
                                case "reqMap":
                                case "random":
                                case "limit":
                                case "cantAccountSharable":
                                case "LvUpWarning":
                                case "canAccountSharable":
                                case "canUseJob":
                                case "createPeriod":
                                case "iconLarge":
                                case "morphItem":
                                case "consumableFrom":
                                case "noExpend":
                                case "sample":
                                case "notPickUpByPet":
                                case "sharableOnce":
                                case "bonusStageItem":
                                case "sampleOffsetY":
                                case "runOnPickup":
                                case "noSale":
                                case "skillCast":
                                case "activateCardSetID":
                                case "summonSoulMobID":
                                case "cursor":
                                case "karma":
                                case "pointCost":
                                case "itemPoint":
                                case "sharedStatCostGrade":
                                case "levelVariation":
                                case "accountShareable":
                                case "extendLimit":
                                case "showMessage":
                                case "mcType":
                                case "consumeItem":
                                case "hybrid":
                                case "mobId":
                                case "lvMin":
                                case "lvMax":
                                case "picture":
                                case "ratef":
                                case "time":
                                case "reqGuildLevel":
                                case "guild":
                                case "randEffect":
                                case "accountShareTag":
                                case "removeEffect":
                                case "forcingItem":
                                case "fixFrameIdx":
                                case "buffItemID":
                                case "removeCharacterInfo":
                                case "nameInfo":
                                case "bgmInfo":
                                case "flip":
                                case "pos":
                                case "randomChair":
                                case "maxLength":
                                case "continuity":
                                case "specificDX":
                                case "groupTWInfo":
                                case "face":
                                case "removeBody":
                                case "mesoChair":
                                case "towerBottom":
                                case "towerTop":
                                case "topOffset":
                                case "craftEXP":
                                case "willEXP":
                                    break;
                                case "tradeBlock":
                                    item.setTradeBlock(intValue != 0);
                                    break;
                                case "notSale":
                                    item.setNotSale(intValue != 0);
                                    break;
                                case "path":
                                    item.setPath(value);
                                    break;
                                case "incRandVol":
                                    item.putScrollStat(incRandVol, intValue);
                                    break;
                                case "success":
                                    item.putScrollStat(success, intValue);
                                    break;
                                case "incSTR":
                                    item.putScrollStat(incSTR, intValue);
                                    break;
                                case "incDEX":
                                    item.putScrollStat(incDEX, intValue);
                                    break;
                                case "incINT":
                                    item.putScrollStat(incINT, intValue);
                                    break;
                                case "incLUK":
                                    item.putScrollStat(incLUK, intValue);
                                    break;
                                case "incPAD":
                                    item.putScrollStat(incPAD, intValue);
                                    break;
                                case "incMAD":
                                    item.putScrollStat(incMAD, intValue);
                                    break;
                                case "incPDD":
                                    item.putScrollStat(incPDD, intValue);
                                    break;
                                case "incMDD":
                                    item.putScrollStat(incMDD, intValue);
                                    break;
                                case "incEVA":
                                    item.putScrollStat(incEVA, intValue);
                                    break;
                                case "incACC":
                                    item.putScrollStat(incACC, intValue);
                                    break;
                                case "incPERIOD":
                                    item.putScrollStat(incPERIOD, intValue);
                                    break;
                                case "incMHP":
                                case "incMaxHP":
                                    item.putScrollStat(incMHP, intValue);
                                    break;
                                case "incMMP":
                                case "incMaxMP":
                                    item.putScrollStat(incMMP, intValue);
                                    break;
                                case "incSpeed":
                                    item.putScrollStat(incSpeed, intValue);
                                    break;
                                case "incJump":
                                    item.putScrollStat(incJump, intValue);
                                    break;
                                case "incReqLevel":
                                    item.putScrollStat(incReqLevel, intValue);
                                    break;
                                case "randOption":
                                    item.putScrollStat(randOption, intValue);
                                    break;
                                case "randstat":
                                case "randStat":
                                    item.putScrollStat(randStat, intValue);
                                    break;
                                case "tuc":
                                    item.putScrollStat(tuc, intValue);
                                    break;
                                case "incIUC":
                                    item.putScrollStat(incIUC, intValue);
                                    break;
                                case "speed":
                                    item.putScrollStat(speed, intValue);
                                    break;
                                case "cursed":
                                    item.putScrollStat(cursed, intValue);
                                    break;
                                case "reqRUC":
                                    item.putScrollStat(reqRUC, intValue);
                                    break;
                                case "quest":
                                    item.setQuest(intValue != 0);
                                    break;
                                case "reqQuestOnProgress":
                                    item.setReqQuestOnProgress(intValue);
                                    break;
                                case "qid":
                                case "questId":
                                    if (value.contains(".") && value.split("[.]").length > 0) {
                                        item.addQuest(Integer.parseInt(value.split("[.]")[0]));
                                    } else {
                                        item.addQuest(intValue);
                                    }
                                    break;
                                case "monsterBook":
                                    item.setMonsterBook(intValue != 0);
                                    break;
                                case "mob":
                                    item.setMob(intValue);
                                    break;
                                case "npc":
                                    item.setNpc(intValue);
                                    break;
                                case "optionType":
                                    item.putScrollStat(optionType, intValue);
                                    break;
                                case "grade":
                                    item.setGrade(intValue);
                                    break;
                                case "spec":
                                    break;
                                case "recover":
                                    item.putScrollStat(recover, intValue);
                                    break;
                                default:
                                    if (INFO_OUT)
                                        log.warn(String.format("Unknown node: %s, value = %s, itemID = %s", name, value, item.getId()));
                            }
                        }
                    }
                    Node reqNode = XMLApi.getFirstChildByNameBF(mainNode, "req");
                    if (reqNode != null) {
                        for (Node req : XMLApi.getAllChildren(reqNode)) {
                            String name = XMLApi.getNamedAttribute(req, "name");
                            String value = XMLApi.getNamedAttribute(req, "value");
                            item.getReqItemIds().add(Integer.parseInt(value));
                        }
                    }
                    Node spec = XMLApi.getFirstChildByNameBF(mainNode, "spec");
                    if (spec != null) {
                        for (Node specNode : XMLApi.getAllChildren(spec)) {
                            String name = XMLApi.getNamedAttribute(specNode, "name");
                            String value = XMLApi.getNamedAttribute(specNode, "value");
                            switch (name) {
                                case "script":
                                    item.setScript(value);
                                    break;
                                case "npc":
                                    item.setScriptNPC(Integer.parseInt(value));
                                    break;
                                case "moveTo":
                                    item.setMoveTo(Integer.parseInt(value));
                                    break;
                                default:
                                    SpecStatType ss = SpecStatType.getSpecStatByName(name);
                                    if (ss != null && value != null) {
                                        item.putSpecStat(ss, Integer.parseInt(value));
                                    } else {
                                        if (INFO_OUT)
                                            log.warn(String.format("Unhandled spec for id %d, name %s, value %s", id, name, value));
                                    }
                            }
                        }
                    }
                    Node reward = XMLApi.getFirstChildByNameBF(mainNode, "reward");
                    if (reward != null) {
                        for (Node rewardNode : XMLApi.getAllChildren(reward)) {
                            ItemRewardInfo iri = new ItemRewardInfo();
                            for (Node rewardInfoNode : XMLApi.getAllChildren(rewardNode)) {
                                String name = XMLApi.getNamedAttribute(rewardInfoNode, "name");
                                String value = XMLApi.getNamedAttribute(rewardInfoNode, "value");
                                if (value == null) {
                                    continue;
                                }
                                value = value.replace("[\r\n]", "").replace("[R8]", "")
                                        .replace("\\r", "").replace("\\n", "");
                                switch (name) {
                                    case "count":
                                        iri.setCount(Integer.parseInt(value));
                                        break;
                                    case "item":
                                        iri.setItem(Integer.parseInt(value));
                                        break;
                                    case "prob":
                                        iri.setProb(Double.parseDouble(value));
                                        break;
                                    case "period":
                                        iri.setPeriod(Integer.parseInt(value));
                                        break;
                                    case "effect":
                                    case "Effect":
                                        iri.setEffect(value);
                                        break;
                                }
                            }
                            item.addItemReward(iri);
                        }
                    }
                    items.put(item.getId(), item);
                }
            }
        }
    }

    public static void loadPetsFromWZ() {
        String wzDir = ServerConstants.WZ_DIR + "/Item.wz";
        File petDir = new File(String.format("%s/%s", wzDir, "Pet"));
        for (File file : petDir.listFiles()) {
            Document doc = XMLApi.getRoot(file);
            int id = Integer.parseInt(file.getName().replace(".img.xml", ""));
            PetInfo pi = new PetInfo();
            pi.setId(id);
            pi.setInventoryType(InventoryType.Use);
            Node infoNode = XMLApi.getFirstChildByNameBF(doc, "info");
            for (Node node : XMLApi.getAllChildren(infoNode)) {
                String name = XMLApi.getNamedAttribute(node, "name");
                String value = XMLApi.getNamedAttribute(node, "value");
                switch (name) {
                    case "icon":
                    case "iconD":
                    case "iconRaw":
                    case "iconRawD":
                    case "hungry":
                    case "nameTag":
                    case "chatBalloon":
                    case "noHungry":
                        break;
                    case "life":
                        pi.setLife(Integer.parseInt(value));
                        break;
                    case "setItemID":
                        pi.setSetItemID(Integer.parseInt(value));
                        break;
                    case "type":
                        pi.setType(Integer.parseInt(value));
                        break;
                    case "limitedLife":
                        pi.setLimitedLife(Integer.parseInt(value));
                        break;
                    case "evol1":
                        pi.setEvol1(Integer.parseInt(value));
                        break;
                    case "evol2":
                        pi.setEvol2(Integer.parseInt(value));
                        break;
                    case "evol3":
                        pi.setEvol3(Integer.parseInt(value));
                        break;
                    case "evol4":
                        pi.setEvol4(Integer.parseInt(value));
                        break;
                    case "evol5":
                        pi.setEvol5(Integer.parseInt(value));
                        break;
                    case "evolProb1":
                        pi.setProbEvol1(Integer.parseInt(value));
                        break;
                    case "evolProb2":
                        pi.setProbEvol2(Integer.parseInt(value));
                        break;
                    case "evolProb3":
                        pi.setProbEvol3(Integer.parseInt(value));
                        break;
                    case "evolProb4":
                        pi.setProbEvol4(Integer.parseInt(value));
                        break;
                    case "evolProb5":
                        pi.setProbEvol5(Integer.parseInt(value));
                        break;
                    case "evolReqItemID":
                        pi.setEvolReqItemID(Integer.parseInt(value));
                        break;
                    case "evolReqPetLvl":
                        pi.setEvolReqPetLvl(Integer.parseInt(value));
                        break;
                    case "evolNo":
                        pi.setEvolNo(Integer.parseInt(value));
                        break;
                    case "permanent":
                        pi.setPermanent(Integer.parseInt(value) != 0);
                        break;
                    case "pickupItem":
                        pi.setPickupItem(Integer.parseInt(value) != 0);
                        break;
                    case "interactByUserAction":
                        pi.setInteractByUserAction(Integer.parseInt(value) != 0);
                        break;
                    case "longRange":
                        pi.setLongRange(Integer.parseInt(value) != 0);
                        break;
                    case "noMoveToLocker":
                        pi.setNoMoveToLocker(Integer.parseInt(value) != 0);
                        break;
                    case "noRevive":
                        pi.setNoRevive(Integer.parseInt(value) != 0);
                        break;
                    case "autoReact":
                        pi.setAutoReact(Integer.parseInt(value) != 0);
                        break;
                    case "pickupAll":
                        pi.setPickupAll(Integer.parseInt(value) != 0);
                        break;
                    case "sweepForDrop":
                        pi.setSweepForDrop(Integer.parseInt(value) != 0);
                        break;
                    case "consumeHP":
                        pi.setConsumeHP(Integer.parseInt(value) != 0);
                        break;
                    case "consumeMP":
                        pi.setConsumeMP(Integer.parseInt(value) != 0);
                        break;
                    case "evol":
                        pi.setEvol(Integer.parseInt(value) != 0);
                        break;
                    case "cash":
                        pi.setCash(Integer.parseInt(value) != 0);
                        pi.setInventoryType(InventoryType.Cash);
                        pi.setCash(true);
                        break;
                    default:
                        if (INFO_OUT)
                            log.warn(String.format("Unhandled pet node, name = %s, value = %s.", name, value));
                        break;
                }
            }
            pets.put(pi.getId(), pi);
            ItemInfo ii = new ItemInfo();
            ii.setId(pi.getId());
            ii.setInventoryType(pi.getInventoryType());
            items.put(ii.getId(), ii);
        }
    }

    public static void loadItemOptionsFromWZ() {
        String wzDir = ServerConstants.WZ_DIR + "/Item.wz";
        String itemOptionDir = String.format("%s/ItemOption.img.xml", wzDir);
        File file = new File(itemOptionDir);
        Document doc = XMLApi.getRoot(file);
        Node node = doc;
        List<Node> nodes = XMLApi.getAllChildren(node);
        for (Node mainNode : XMLApi.getAllChildren(nodes.get(0))) {
            ms.maplestory.boot.client.character.Inventory.ItemOption io = new ms.maplestory.boot.client.character.Inventory.ItemOption();
            String nodeName = XMLApi.getNamedAttribute(mainNode, "name");
            io.setId(Integer.parseInt(nodeName));
            Node infoNode = XMLApi.getFirstChildByNameBF(mainNode, "info");
            if (infoNode != null) {
                for (Node infoChild : XMLApi.getAllChildren(infoNode)) {
                    String name = XMLApi.getNamedAttribute(infoChild, "name");
                    String value = XMLApi.getNamedAttribute(infoChild, "value");
                    switch (name) {
                        case "optionType":
                            io.setOptionType(Integer.parseInt(value));
                            break;
                        case "reqLevel":
                            io.setReqLevel(Integer.parseInt(value));
                            break;
                    }
                }
            }
            Node levelNode = XMLApi.getFirstChildByNameBF(mainNode, "level");
            if (levelNode != null) {
                for (Node levelChild : XMLApi.getAllChildren(levelNode)) {
                    int level = Integer.parseInt(XMLApi.getNamedAttribute(levelChild, "name"));
                    for (Node levelInfoNode : XMLApi.getAllChildren(levelChild)) {
                        String name = XMLApi.getNamedAttribute(levelInfoNode, "name");
                        String stringValue = XMLApi.getNamedAttribute(levelInfoNode, "value");
                        int value = 0;
                        if (Util.isNumber(stringValue)) {
                            value = Integer.parseInt(stringValue);
                        }
                        switch (name) {
                            case "incSTR":
                                io.addStatValue(level, BaseStatType.incSTR, value);
                                break;
                            case "incDEX":
                                io.addStatValue(level, BaseStatType.incDEX, value);
                                break;
                            case "incINT":
                                io.addStatValue(level, BaseStatType.incINT, value);
                                break;
                            case "incLUK":
                                io.addStatValue(level, BaseStatType.incLUK, value);
                                break;
                            case "incMHP":
                                io.addStatValue(level, BaseStatType.incMHP, value);
                                break;
                            case "incMMP":
                                io.addStatValue(level, BaseStatType.incMMP, value);
                                break;
                            case "incACC":
                                io.addStatValue(level, BaseStatType.incACC, value);
                                break;
                            case "incEVA":
                                io.addStatValue(level, BaseStatType.incEVA, value);
                                break;
                            case "incSpeed":
                                io.addStatValue(level, BaseStatType.incSpeed, value);
                                break;
                            case "incJump":
                                io.addStatValue(level, BaseStatType.incJump, value);
                                break;
                            case "incPAD":
                                io.addStatValue(level, BaseStatType.incPAD, value);
                                break;
                            case "incMAD":
                                io.addStatValue(level, BaseStatType.incMAD, value);
                                break;
                            case "incPDD":
                                io.addStatValue(level, BaseStatType.incPDD, value);
                                break;
                            case "incMDD":
                                io.addStatValue(level, BaseStatType.incMDD, value);
                                break;
                            case "incCr":
                                io.addStatValue(level, BaseStatType.incCr, value);
                                break;
                            case "incPADr":
                                io.addStatValue(level, BaseStatType.incPADr, value);
                                break;
                            case "incMADr":
                                io.addStatValue(level, BaseStatType.incMADr, value);
                                break;
                            case "incSTRr":
                                io.addStatValue(level, BaseStatType.incSTRr, value);
                                break;
                            case "incDEXr":
                                io.addStatValue(level, BaseStatType.incDEXr, value);
                                break;
                            case "incINTr":
                                io.addStatValue(level, BaseStatType.incINTr, value);
                                break;
                            case "incLUKr":
                                io.addStatValue(level, BaseStatType.incLUKr, value);
                                break;
                            case "ignoreTargetDEF":
                                io.addStatValue(level, BaseStatType.ignoreTargetDEF, value);
                                break;
                            case "incDAMr":
                                io.addStatValue(level, BaseStatType.incDAMr, value);
                                break;
                            case "boss":
                                Node incDamgNode = XMLApi.getFirstChildByNameDF(levelChild, "incDAMr");
                                if (incDamgNode != null) {
                                    value = Integer.parseInt(XMLApi.getNamedAttribute(incDamgNode, "value"));
                                }
                                io.addStatValue(level, BaseStatType.IncBossDAMr, value);
                                break;
                            case "incAllskill":
                                io.addStatValue(level, BaseStatType.incAllSkill, value);
                                break;
                            case "incMHPr":
                                io.addStatValue(level, BaseStatType.incMHPr, value);
                                break;
                            case "incMMPr":
                                io.addStatValue(level, BaseStatType.incMMPr, value);
                                break;
                            case "incACCr":
                                io.addStatValue(level, BaseStatType.incACCr, value);
                                break;
                            case "incEVAr":
                                io.addStatValue(level, BaseStatType.incEVAr, value);
                                break;
                            case "incPDDr":
                                io.addStatValue(level, BaseStatType.incPDDr, value);
                                break;
                            case "incMDDr":
                                io.addStatValue(level, BaseStatType.incMDDr, value);
                                break;
                            case "RecoveryHP":
                                io.addStatValue(level, BaseStatType.RecoveryHP, value);
                                break;
                            case "RecoveryMP":
                                io.addStatValue(level, BaseStatType.RecoveryMP, value);
                                break;
                            case "RecoveryUP":
                                io.addStatValue(level, BaseStatType.RecoveryUP, value);
                                break;
                            case "mpconReduce":
                                io.addStatValue(level, BaseStatType.mpconReduce, value);
                                break;
                            case "incMesoProp":
                                io.addStatValue(level, BaseStatType.incMesoProp, value);
                                break;
                            case "incRewardProp":
                                io.addStatValue(level, BaseStatType.incRewardProp, value);
                                break;
                            case "prop":
                                io.addMiscValue(level, ItemOptionType.prop, value);
                                break;
                            case "face":
                                io.addMiscValue(level, ItemOptionType.face, value);
                                break;
                            case "time":
                                io.addMiscValue(level, ItemOptionType.time, value);
                                break;
                            case "HP":
                                io.addMiscValue(level, ItemOptionType.hpRecoveryOnHit, value);
                                break;
                            case "MP":
                                io.addMiscValue(level, ItemOptionType.mpRecoveryOnHit, value);
                                break;
                            case "attackType":
                                io.addMiscValue(level, ItemOptionType.attackType, value);
                                break;
                            case "level":
                                io.addMiscValue(level, ItemOptionType.level, value);
                                break;
                            case "ignoreDAM":
                                io.addMiscValue(level, ItemOptionType.ignoreDam, value);
                                break;
                        }
                    }

                }
            }
            itemOptions.put(io.getId(), io);
        }
    }

    @Saver(varName = "equips")
    public static void saveEquipsToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        for (Equip equip : equips.values()) {
            try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(dir + "/" + equip.getItemId() + ".dat"))) {
                dataOutputStream.writeInt(equip.getItemId());
                dataOutputStream.writeUTF(equip.getISlot());
                dataOutputStream.writeUTF(equip.getVSlot());
                dataOutputStream.writeInt(equip.getReqJob());
                dataOutputStream.writeInt(equip.getReqLevel());
                dataOutputStream.writeInt(equip.getReqSTR());
                dataOutputStream.writeInt(equip.getReqDEX());
                dataOutputStream.writeInt(equip.getReqINT());
                dataOutputStream.writeInt(equip.getReqLUK());
                dataOutputStream.writeInt(equip.getReqPOP());
                dataOutputStream.writeInt(equip.getIncSTR());
                dataOutputStream.writeInt(equip.getIncDEX());
                dataOutputStream.writeInt(equip.getIncINT());
                dataOutputStream.writeInt(equip.getIncLUK());
                dataOutputStream.writeInt(equip.getIncPDD());
                dataOutputStream.writeInt(equip.getIncMDD());
                dataOutputStream.writeInt(equip.getIncMHP());
                dataOutputStream.writeInt(equip.getIncMHPr());
                dataOutputStream.writeInt(equip.getIncMMP());
                dataOutputStream.writeInt(equip.getIncMMPr());
                dataOutputStream.writeInt(equip.getIncPAD());
                dataOutputStream.writeInt(equip.getIncMAD());
                dataOutputStream.writeInt(equip.getIncEVA());
                dataOutputStream.writeInt(equip.getIncACC());
                dataOutputStream.writeInt(equip.getIncSpeed());
                dataOutputStream.writeInt(equip.getIncJump());
                dataOutputStream.writeInt(equip.getTuc());
                dataOutputStream.writeInt(equip.getSetItemID());
                dataOutputStream.writeInt(equip.getPrice());
                dataOutputStream.writeInt(equip.getAttackSpeed());
                dataOutputStream.writeBoolean(equip.isCash());
                dataOutputStream.writeBoolean(equip.isExpireOnLogout());
                dataOutputStream.writeBoolean(equip.isNotSale());
                dataOutputStream.writeBoolean(equip.isOnly());
                dataOutputStream.writeBoolean(equip.isTradeBlock());
                dataOutputStream.writeBoolean(equip.isHasIucMax());
                if (equip.isHasIucMax()) {
                    dataOutputStream.writeShort(equip.getIucMax());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void saveItemsToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        for (ItemInfo ii : items.values()) {
            try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(new File(dir + "/" + ii.getId() + ".dat")))) {
                dataOutputStream.writeInt(ii.getId());
                dataOutputStream.writeUTF(ii.getInventoryType().toString());
                dataOutputStream.writeBoolean(ii.isCash());
                dataOutputStream.writeInt(ii.getPrice());
                dataOutputStream.writeInt(ii.getSlotMax());
                dataOutputStream.writeBoolean(ii.isTradeBlock());
                dataOutputStream.writeBoolean(ii.isNotSale());
                dataOutputStream.writeUTF(ii.getPath());
                dataOutputStream.writeBoolean(ii.isQuest());
                dataOutputStream.writeInt(ii.getReqQuestOnProgress());
                dataOutputStream.writeBoolean(ii.isMonsterBook());
                dataOutputStream.writeInt(ii.getMob());
                dataOutputStream.writeInt(ii.getNpc());
                dataOutputStream.writeUTF(ii.getScript());
                dataOutputStream.writeInt(ii.getScriptNPC());
                dataOutputStream.writeShort(ii.getScrollStats().size());
                for (Map.Entry<ScrollStatType, Integer> entry : ii.getScrollStats().entrySet()) {
                    dataOutputStream.writeUTF(entry.getKey().toString());
                    dataOutputStream.writeInt(entry.getValue());
                }
                dataOutputStream.writeShort(ii.getSpecStats().size());
                for (Map.Entry<SpecStatType, Integer> entry : ii.getSpecStats().entrySet()) {
                    dataOutputStream.writeUTF(entry.getKey().toString());
                    dataOutputStream.writeInt(entry.getValue());
                }
                dataOutputStream.writeShort(ii.getQuestIDs().size());
                for (int i : ii.getQuestIDs()) {
                    dataOutputStream.writeInt(i);
                }
                dataOutputStream.writeShort(ii.getReqItemIds().size());
                for (int i : ii.getReqItemIds()) {
                    dataOutputStream.writeInt(i);
                }
                dataOutputStream.writeShort(ii.getItemRewardInfos().size());
                for (ItemRewardInfo iri : ii.getItemRewardInfos()) {
                    dataOutputStream.writeInt(iri.getCount());
                    dataOutputStream.writeInt(iri.getItem());
                    dataOutputStream.writeDouble(iri.getProb());
                    dataOutputStream.writeInt(iri.getPeriod());
                    dataOutputStream.writeUTF(iri.getEffect());
                }
                dataOutputStream.writeInt(ii.getMoveTo());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void savePetsToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        for (PetInfo pi : pets.values()) {
            File file = new File(String.format("%s/%d.dat", dir, pi.getId()));
            try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
                dos.writeInt(pi.getId());
                dos.writeByte(pi.getInventoryType().getValue());
                dos.writeInt(pi.getLife());
                dos.writeInt(pi.getSetItemID());
                dos.writeInt(pi.getLimitedLife());
                dos.writeInt(pi.getType());
                dos.writeInt(pi.getEvolReqItemID());
                dos.writeInt(pi.getEvolNo());
                dos.writeInt(pi.getEvol1());
                dos.writeInt(pi.getEvol2());
                dos.writeInt(pi.getEvol3());
                dos.writeInt(pi.getEvol4());
                dos.writeInt(pi.getEvol5());
                dos.writeInt(pi.getProbEvol1());
                dos.writeInt(pi.getProbEvol2());
                dos.writeInt(pi.getProbEvol3());
                dos.writeInt(pi.getProbEvol4());
                dos.writeInt(pi.getProbEvol5());
                dos.writeInt(pi.getEvolReqPetLvl());
                dos.writeBoolean(pi.isNoRevive());
                dos.writeBoolean(pi.isCash());
                dos.writeBoolean(pi.isPermanent());
                dos.writeBoolean(pi.isPickupItem());
                dos.writeBoolean(pi.isInteractByUserAction());
                dos.writeBoolean(pi.isLongRange());
                dos.writeBoolean(pi.isEvol());
                dos.writeBoolean(pi.isAutoReact());
                dos.writeBoolean(pi.isPickupAll());
                dos.writeBoolean(pi.isSweepForDrop());
                dos.writeBoolean(pi.isConsumeMP());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void saveItemOptionsToDat(String dir) {
        File file = new File(String.format("%s/itemOptions.dat", dir));
        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
            dos.writeInt(itemOptions.size());
            for (ms.maplestory.boot.client.character.Inventory.ItemOption io : itemOptions.values()) {
                dos.writeInt(io.getId());
                dos.writeInt(io.getOptionType());
                dos.writeInt(io.getReqLevel());
                dos.writeShort(io.getStatValuesPerLevel().size());
                for (Map.Entry<Integer, Map<BaseStatType, Double>> entry1 : io.getStatValuesPerLevel().entrySet()) {
                    dos.writeInt(entry1.getKey());
                    dos.writeShort(entry1.getValue().size());
                    for (Map.Entry<BaseStatType, Double> entry2 : entry1.getValue().entrySet()) {
                        dos.writeInt(entry2.getKey().ordinal());
                        dos.writeDouble(entry2.getValue());
                    }
                }
                dos.writeShort(io.getMiscValuesPerLevel().size());
                for (Map.Entry<Integer, Map<ItemOptionType, Integer>> entry1 : io.getMiscValuesPerLevel().entrySet()) {
                    dos.writeInt(entry1.getKey());
                    dos.writeShort(entry1.getValue().size());
                    for (Map.Entry<ItemOptionType, Integer> entry2 : entry1.getValue().entrySet()) {
                        dos.writeInt(entry2.getKey().ordinal());
                        dos.writeInt(entry2.getValue());
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static ItemInfo loadItemFromDat(File file) {
        ItemInfo itemInfo = null;
        try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file))) {
            itemInfo = new ItemInfo();
            itemInfo.setId(dataInputStream.readInt());
            itemInfo.setInventoryType(InventoryType.getTypeByString(dataInputStream.readUTF()));
            itemInfo.setCash(dataInputStream.readBoolean());
            itemInfo.setPrice(dataInputStream.readInt());
            itemInfo.setSlotMax(dataInputStream.readInt());
            itemInfo.setTradeBlock(dataInputStream.readBoolean());
            itemInfo.setNotSale(dataInputStream.readBoolean());
            itemInfo.setPath(dataInputStream.readUTF());
            itemInfo.setQuest(dataInputStream.readBoolean());
            itemInfo.setReqQuestOnProgress(dataInputStream.readInt());
            itemInfo.setMonsterBook(dataInputStream.readBoolean());
            itemInfo.setMob(dataInputStream.readInt());
            itemInfo.setNpc(dataInputStream.readInt());
            itemInfo.setScript(dataInputStream.readUTF());
            itemInfo.setScriptNPC(dataInputStream.readInt());
            short size = dataInputStream.readShort();
            for (int i = 0; i < size; i++) {
                ScrollStatType ss = ScrollStatType.getScrollStatByString(dataInputStream.readUTF());
                int val = dataInputStream.readInt();
                itemInfo.putScrollStat(ss, val);
            }
            size = dataInputStream.readShort();
            for (int i = 0; i < size; i++) {
                SpecStatType ss = SpecStatType.getSpecStatByName(dataInputStream.readUTF());
                int val = dataInputStream.readInt();
                itemInfo.putSpecStat(ss, val);
            }
            size = dataInputStream.readShort();
            for (int i = 0; i < size; i++) {
                itemInfo.addQuest(dataInputStream.readInt());
            }

            size = dataInputStream.readShort();
            for (int i = 0; i < size; i++) {
                itemInfo.getReqItemIds().add(dataInputStream.readInt());
            }
            size = dataInputStream.readShort();
            for (int i = 0; i < size; i++) {
                ItemRewardInfo iri = new ItemRewardInfo();
                iri.setCount(dataInputStream.readInt());
                iri.setItem(dataInputStream.readInt());
                iri.setProb(dataInputStream.readDouble());
                iri.setPeriod(dataInputStream.readInt());
                iri.setEffect(dataInputStream.readUTF());
                itemInfo.addItemReward(iri);
            }
            itemInfo.setMoveTo(dataInputStream.readInt());
            items.put(itemInfo.getId(), itemInfo);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return itemInfo;
    }

    @Loader(varName = "startingItems")
    public static void loadStartingItems(File file, boolean exists) {
        if (!exists) {
            loadStartingItemsFromWZ();
            saveStartingItemsToDat(ServerConstants.DAT_DIR);
        } else {
            try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {
                int size = dis.readInt();
                for (int i = 0; i < size; i++) {
                    startingItems.add(dis.readInt());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static void loadStartingItemsFromWZ() {
        String wzDir = ServerConstants.WZ_DIR + "/Etc.wz";
        String itemOptionDir = String.format("%s/MakeCharInfo.img.xml", wzDir);
        File file = new File(itemOptionDir);
        startingItems.addAll(searchForStartingItems(XMLApi.getRoot(file)));
    }

    private static void saveStartingItemsToDat(String dir) {
        File file = new File(String.format("%s/startingItems.dat", dir));
        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
            dos.writeInt(startingItems.size());
            for (int i : startingItems) {
                dos.writeInt(i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void addItemInfo(ItemInfo ii) {
        items.put(ii.getId(), ii);
    }

    public static ItemInfo getItemInfoByID(int itemID) {
        ItemInfo ii = items.getOrDefault(itemID, null);
        if (ii == null) {
            File file = new File(String.format("%s/items/%d.dat", ServerConstants.DAT_DIR, itemID));
            if (!file.exists()) {
                return null;
            } else {
                ii = loadItemFromDat(file);
            }
        }
        return ii;
    }

    private static Set<Integer> searchForStartingItems(Node n) {
        List<Node> subNodes = XMLApi.getAllChildren(n);
        for (Node node : subNodes) {
            String name = XMLApi.getNamedAttribute(node, "name");
            String value = XMLApi.getNamedAttribute(node, "value");
            if (Util.isNumber(name) && value != null && Util.isNumber(value)) {
                startingItems.add(Integer.parseInt(value));
            }
            startingItems.addAll(searchForStartingItems(node));
        }
        return startingItems;
    }

    public static boolean isStartingItems(int[] items) {
        for (int item : items) {
            if (!isStartingItem(item)) {
                return false;
            }
        }
        return true;
    }

    public static boolean isStartingItem(int item) {
        return startingItems.contains(item);
    }

    public static Equip getEquipDeepCopyFromId(int itemId, boolean randomizeStats) {
        Equip e = getEquipById(itemId);
        Equip ret = e == null ? null : e.deepCopy();
        if (ret != null) {
            ret.setQuantity(1);
            ret.setType(ItemType.Equip);
            ret.setInventoryType(InventoryType.Equip);
            if (randomizeStats) {
                // TODO::装备随机属性
            }
        }
        return ret;
    }

    public static Equip getEquipById(int itemId) {
        return equips.getOrDefault(itemId, getEquipFromFile(itemId));
    }

    private static Equip getEquipFromFile(int itemId) {
        String fieldDir = String.format("%s/equips/%d.dat", ServerConstants.DAT_DIR, itemId);
        File file = new File(fieldDir);
        if (!file.exists()) {
            return null;
        } else {
            return readEquipFromDat(file);
        }
    }

    private static Equip readEquipFromDat(File file) {
        Equip equip = null;
        try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file))) {
            equip = new Equip();
            equip.setItemId(dataInputStream.readInt());
            equip.setISlot(dataInputStream.readUTF());
            equip.setVSlot(dataInputStream.readUTF());
            equip.setReqJob(dataInputStream.readInt());
            equip.setReqLevel(dataInputStream.readInt());
            equip.setReqSTR(dataInputStream.readInt());
            equip.setReqDEX(dataInputStream.readInt());
            equip.setReqINT(dataInputStream.readInt());
            equip.setReqLUK(dataInputStream.readInt());
            equip.setReqPOP(dataInputStream.readInt());
            equip.setIncSTR(dataInputStream.readInt());
            equip.setIncDEX(dataInputStream.readInt());
            equip.setIncINT(dataInputStream.readInt());
            equip.setIncLUK(dataInputStream.readInt());
            equip.setIncPDD(dataInputStream.readInt());
            equip.setIncMDD(dataInputStream.readInt());
            equip.setIncMHP(dataInputStream.readInt());
            equip.setIncMHPr(dataInputStream.readInt());
            equip.setIncMMP(dataInputStream.readInt());
            equip.setIncMMPr(dataInputStream.readInt());
            equip.setIncPAD(dataInputStream.readInt());
            equip.setIncMAD(dataInputStream.readInt());
            equip.setIncEVA(dataInputStream.readInt());
            equip.setIncACC(dataInputStream.readInt());
            equip.setIncSpeed(dataInputStream.readInt());
            equip.setIncJump(dataInputStream.readInt());
            equip.setTuc(dataInputStream.readInt());
            equip.setSetItemID(dataInputStream.readInt());
            equip.setPrice(dataInputStream.readInt());
            equip.setAttackSpeed(dataInputStream.readInt());
            equip.setCash(dataInputStream.readBoolean());
            equip.setExpireOnLogout(dataInputStream.readBoolean());
            equip.setNotSale(dataInputStream.readBoolean());
            equip.setOnly(dataInputStream.readBoolean());
            equip.setTradeBlock(dataInputStream.readBoolean());
            equip.setHasIucMax(dataInputStream.readBoolean());
            if (equip.isHasIucMax()) {
                equip.setIucMax(dataInputStream.readShort());
            }
            equips.put(equip.getItemId(), equip);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return equip;
    }
}
